/**
 * @descript Date 类型数据处理工具函数集
 *
 * @author 济南晨霜信息技术有限公司
 * @mobile 18560000860 / 18754137913
 *
 * @list 函数列表
 * ========================================================================================================================
 * = dateGet                : 获取日期对象
 * = dateGetDiffYears       : 获取两个日期相差的年数
 * = dateGetDiffMonths      : 获取两个日期相差的月数
 * = dateGetDiffWeeks       : 获取两个日期相差的周数
 * = dateGetDiffDays        : 获取两个日期相差的天数
 * = dateGetDiffHours       : 获取两个日期相差的小时数
 * = dateGetDiffMinutes     : 获取两个日期相差的分钟数
 * = dateGetDiffSeconds     : 获取两个日期相差的秒数
 * = dateGetDiffMilliSeconds: 获取两个日期相差的毫秒数
 * = dateGetDiff            : 获取两个时间的差值
 * = dateGetCountdownData   : 获取两个时间的倒计时数据
 * = dateGetCountdownString : 获取两个时间的倒计时字符串
 * ========================================================================================================================
 */
import { isNumber, isString, isSecondTimestamp, isMillisecondTimestamp } from "./validate";

/**
 * 获取日期对象
 * Tips: 传入的可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number | String} date 日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Date | null} 返回 Date 对象或 null
 */
export const dateGet = (date) => {
    if (!date) return null;
    if (isNumber(date)) {
        if (isSecondTimestamp(date)) return new Date(date * 1000);
        if (isMillisecondTimestamp(date)) return new Date(date);
        return null;
    }
    if (isString(date)) {
        date = date.replace(/-/g, "/");
        return new Date(date);
    }
    return date;
};

/**
 * 获取两个日期相差的年数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的年数
 */
export const dateGetDiffYears = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const startYear = startDate.getFullYear();
    const endYear = endDate.getFullYear();
    return endYear - startYear;
};

/**
 * 获取两个日期相差的月数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的月数
 */
export const dateGetDiffMonths = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const diffYears = dateGetDiffYears(startDate, endDate);
    const startMonth = startDate.getMonth();
    const endMonth = endDate.getMonth();
    return diffYears * 12 + endMonth - startMonth;
};

/**
 * 获取两个日期相差的周数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的周数
 */
export const dateGetDiffWeeks = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const diffMilliseconds = endDate.getTime() - startDate.getTime();
    return parseInt(diffMilliseconds / 1000 / 60 / 60 / 24 / 7);
};

/**
 * 获取两个日期相差的天数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的天数
 */
export const dateGetDiffDays = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const diffMilliseconds = endDate.getTime() - startDate.getTime();
    return parseInt(diffMilliseconds / 1000 / 60 / 60 / 24);
};

/**
 * 获取两个日期相差的小时数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的小时数
 */
export const dateGetDiffHours = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const diffMilliseconds = endDate.getTime() - startDate.getTime();
    return parseInt(diffMilliseconds / 1000 / 60 / 60);
};

/**
 * 获取两个日期相差的分钟数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的分钟数
 */
export const dateGetDiffMinutes = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const diffMilliseconds = endDate.getTime() - startDate.getTime();
    return parseInt(diffMilliseconds / 1000 / 60);
};

/**
 * 获取两个日期相差的秒数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的秒数
 */
export const dateGetDiffSeconds = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    const diffMilliseconds = endDate.getTime() - startDate.getTime();
    return parseInt(diffMilliseconds / 1000);
};

/**
 * 获取两个日期相差的毫秒数
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Number} 两个日期相差的毫秒数
 */
export const dateGetDiffMilliSeconds = (startDate, endDate) => {
    if (!startDate || !endDate) return 0;
    startDate = dateGet(startDate);
    endDate = dateGet(endDate);
    return parseInt(endDate.getTime() - startDate.getTime());
};

/**
 * 获取两个时间的差值
 * @param {Date | Number |  String} startDate 开始日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {Date | Number |  String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @return {Object} 两个日期的差值，包括年、月、周、日、时、分、秒、毫秒
 */
export const dateGetDiff = (startDate, endDate) => {
    return {
        years: dateGetDiffYears(startDate, endDate),
        months: dateGetDiffMonths(startDate, endDate),
        weeks: dateGetDiffWeeks(startDate, endDate),
        days: dateGetDiffDays(startDate, endDate),
        hours: dateGetDiffHours(startDate, endDate),
        minutes: dateGetDiffMinutes(startDate, endDate),
        seconds: dateGetDiffSeconds(startDate, endDate),
        milliseconds: dateGetDiffMilliSeconds(startDate, endDate)
    };
};

/**
 * 获取两个时间的倒计时数据
 * Tips: 相对于当前时间，倒计时结束还有多久
 * @param {Date | Number | String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {String} level 最大计算级别，可选项：week、day、hour、minute、second、millisecond，默认: day
 * @return {Object} 倒计时数据，根据传入的 level，可能包括周、天、时、分、秒、毫秒
 */
export const dateGetCountdownData = (endDate, level = "day") => {
    // 当前时间
    const nowDate = new Date();

    // 获取结束时间和当前时间的差值
    const diff = dateGetDiff(nowDate, endDate);

    // 计算到周
    if (level === "week") {
        diff.weeks = diff.weeks;
        diff.days = diff.days % 7;
        diff.hours = diff.hours % 24;
        diff.minutes = diff.minutes % 60;
        diff.seconds = diff.seconds % 60;
        diff.milliseconds = diff.milliseconds % 1000;
        return diff;
    }

    // 计算到天
    if (level === "day") {
        diff.weeks = 0;
        diff.days = diff.days;
        diff.hours = diff.hours % 24;
        diff.minutes = diff.minutes % 60;
        diff.seconds = diff.seconds % 60;
        diff.milliseconds = diff.milliseconds % 1000;
        return diff;
    }

    // 计算到小时
    if (level === "hour") {
        diff.weeks = 0;
        diff.days = 0;
        diff.hours = diff.hours;
        diff.minutes = diff.minutes % 60;
        diff.seconds = diff.seconds % 60;
        diff.milliseconds = diff.milliseconds % 1000;
        return diff;
    }

    // 计算到分钟
    if (level === "minute") {
        diff.weeks = 0;
        diff.days = 0;
        diff.hours = 0;
        diff.minutes = diff.minutes;
        diff.seconds = diff.seconds % 60;
        diff.milliseconds = diff.milliseconds % 1000;
        return diff;
    }

    // 计算到秒
    if (level === "second") {
        diff.weeks = 0;
        diff.days = 0;
        diff.hours = 0;
        diff.minutes = 0;
        diff.seconds = diff.seconds;
        diff.milliseconds = diff.milliseconds % 1000;
        return diff;
    }

    // 计算到毫秒
    if (level === "millisecond") {
        diff.weeks = 0;
        diff.days = 0;
        diff.hours = 0;
        diff.minutes = 0;
        diff.seconds = 0;
        diff.milliseconds = diff.milliseconds;
        return diff;
    }

    // 默认
    return diff;
};

/**
 * 获取两个时间的倒计时字符串
 * Tips: 相对于当前时间，倒计时结束还有多久
 * @param {Date | Number | String} endDate 结束日期，可以是 Date 类型，也可以是时间戳、日期字符串
 * @param {String} formatString 格式字符串，例如 “距离倒计时结束还剩{WW}周{DD | D}天{HH | D}时{MM | M}分{SS | S}秒{MSS | MS}毫秒”，注意，字母大写，双字母（毫秒三字母）表示补零，单字母（毫秒双字母）表示不补零
 * @return {Object} 格式化后的字符串
 */
export const dateGetCountdownString = (endDate, formatString = "{DD}天{HH}时{MM}分{SS}秒") => {
    // 获取倒计时数据级别
    let level = "day";

    // 判断是否是周级别
    if (formatString.includes("{WW}") || formatString.includes("{W}")) level = "week";
    // 判断是否是天级别
    else if (formatString.includes("{DD}") || formatString.includes("{D}")) level = "day";
    // 判断是否是小时级别
    else if (formatString.includes("{HH}") || formatString.includes("{H}")) level = "hour";
    // 判断是否是分钟级别
    else if (formatString.includes("{MM}") || formatString.includes("{M}")) level = "minute";
    // 判断是否是秒级别
    else if (formatString.includes("{SS}") || formatString.includes("{S}")) level = "second";
    // 判断是否是毫秒级别
    else if (formatString.includes("{MSS}") || formatString.includes("{MS}")) level = "millisecond";

    // 获取倒计时数据
    const countdownData = dateGetCountdownData(endDate, level);

    // 不能出现负数
    if (countdownData.weeks <= 0) countdownData.weeks = 0;
    if (countdownData.days <= 0) countdownData.days = 0;
    if (countdownData.hours <= 0) countdownData.hours = 0;
    if (countdownData.minutes <= 0) countdownData.minutes = 0;
    if (countdownData.seconds <= 0) countdownData.seconds = 0;
    if (countdownData.milliseconds <= 0) countdownData.milliseconds = 0;

    // 格式化字符串
    formatString = formatString
        .replace("{WW}", countdownData.weeks.toString().padStart(2, "0"))
        .replace("{W}", countdownData.weeks.toString())
        .replace("{DD}", countdownData.days.toString().padStart(2, "0"))
        .replace("{D}", countdownData.days.toString())
        .replace("{HH}", countdownData.hours.toString().padStart(2, "0"))
        .replace("{H}", countdownData.hours.toString())
        .replace("{MM}", countdownData.minutes.toString().padStart(2, "0"))
        .replace("{M}", countdownData.minutes.toString())
        .replace("{SS}", countdownData.seconds.toString().padStart(2, "0"))
        .replace("{S}", countdownData.seconds.toString())
        .replace("{MSS}", countdownData.milliseconds.toString().padStart(3, "0"))
        .replace(
            "{MS}",
            parseInt(countdownData.milliseconds / 10)
                .toString()
                .padStart(2, "0")
        );

    return formatString;
};
